package org.hypirinha.utils.html2hypirinha;

import static org.apache.commons.collections15.CollectionUtils.collect;
import static org.apache.commons.collections15.CollectionUtils.select;
import static org.apache.commons.lang.StringEscapeUtils.unescapeHtml;

import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;

import org.apache.commons.collections15.Predicate;
import org.apache.commons.collections15.Transformer;
import org.apache.commons.lang.StringUtils;
import org.dom4j.Attribute;
import org.dom4j.Document;
import org.dom4j.Element;
import org.dom4j.Node;
import org.dom4j.Text;

public class BuilderStatements {
	private List<String> statements = new ArrayList<String>();
	private Map<String, Integer> elementCounts = new HashMap<String, Integer>();
	
	public BuilderStatements(Document document) {
		trimWhitespaceTextNodes(document.getRootElement());
		descendIntoElement(document.getRootElement(), "html");
	}

	public String[] asStrings() {
		return statements.toArray(new String[statements.size()]);
	}
	
	private void trimWhitespaceTextNodes(Element element) {
		List<Node> nodesToBeRemoved = new ArrayList<Node>();
		for (Iterator<?> iterator = element.nodeIterator(); iterator.hasNext();) {
			Node node = (Node) iterator.next();
			if (node instanceof Text) {
				String text = ((Text) node).getText();
				if (text.trim().equals("")) {
					nodesToBeRemoved.add(node);
				}
			}
			if (node instanceof Element) {
				trimWhitespaceTextNodes((Element) node);
			}
		}
		for (Node node : nodesToBeRemoved) {
			element.remove(node);
		}
	}

	private void descendIntoElement(Element parentElement, String parentName) {
		for (Iterator<?> iterator = parentElement.nodeIterator(); iterator
				.hasNext();) {
			Node node = (Node) iterator.next();

			if (node instanceof Element) {
				Element element = (Element) node;

				String variableDeclaration = "";
				String childVarName = null;
				Element branchElement = findBranchElement(element);
				if (branchElement != null) {
					childVarName = buildVariableName(branchElement.getName());
					variableDeclaration = StringUtils.capitalize(branchElement.getName()) + " " + childVarName + " = ";
				}
				statements.add(variableDeclaration + parentName + createElement(element) + ";");
				if (branchElement != null) {
					descendIntoElement(branchElement, childVarName);
				}
			} else if (node instanceof Text) {
				Text textNode = (Text) node;

				String text = textNode.getText();
				if (!text.trim().equals("")) {
					statements.add(parentName + createText(text) + ";");
				}
			}
		}
	}

	private String createText(String text) {
		return ".text(\"" + unescapeHtml(text).replaceAll("\\s+", " ").trim() + "\")";
	}

	@SuppressWarnings("unchecked")
	private String createElement(Element element) {
		String attributeList = attributeList(element.attributes());
		String expression = "." + element.getName() + "(" + attributeList + ")";

		if (element.nodeCount() == 1) {
			Node node = (Node) element.nodeIterator().next();
			if (node instanceof Text) {
				return expression + createText(((Text) node).getText());
			} else if (node instanceof Element) {
				return expression + createElement((Element) node);
			}
		}
		return expression;
	}

	private Element findBranchElement(Element element) {
		if (element.nodeCount() == 0) {
			return null;
		} else if (element.nodeCount() == 1
				&& element.nodeIterator().next() instanceof Text) {
			return null;
		} else if (element.nodeCount() == 1
				&& element.nodeIterator().next() instanceof Element) {
			return findBranchElement((Element) element.nodeIterator().next());
		} else {
			return element;
		}
	}

	private String attributeList(List<Attribute> list) {
		Collection<Attribute> htmlAttribs = select(list,
				new Predicate<Attribute>() {
					public boolean evaluate(Attribute o) {
						return !"http://www.w3.org/XML/1998/namespace".equals(o
								.getNamespaceURI());
					}
				});
		Collection<String> strings = collect(htmlAttribs,
				new Transformer<Attribute, String>() {
					public String transform(Attribute attribute) {
						return escapeAttr(attribute.getName()) + "(\""
								+ unescapeHtml(attribute.getValue()).replaceAll("\"", "\\\\\"") + "\")";
					}
				});
		return StringUtils.join(strings, ", ");
	}

	private String escapeAttr(String name) {
		if ("class".equals(name))
			return "classs";
		if ("for".equals(name))
			return "forr";
		if ("http-equiv".equals(name))
			return "httpequiv";

		return name;
	}

	private String buildVariableName(String elementType) {
		String variableName;
		if (elementCounts.containsKey(elementType)) {
			variableName = elementType + elementCounts.get(elementType);
			elementCounts.put(elementType, elementCounts.get(elementType) + 1);
		} else {
			variableName = elementType;
			elementCounts.put(elementType, 1);
		}
		return variableName;
	}
}